/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package simplemetrosystem.control;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JOptionPane;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;
import org.xml.sax.SAXException;
import simplemetrosystem.control.Exception.ConfigureException;
import simplemetrosystem.model.Exceptions.NameMapException;
import simplemetrosystem.model.charlanguage.NameMap;
import simplemetrosystem.view.MetroFrame;

/**
 *
 * @author Eric Qian
 * 
 * 程序配置文件读取存储类,该类本生为单例属性,只能由该类得静态方法得到该类的实例.
 * 配置文件为XML格式.
 * 标签有<NameMap>中英文名称
 * 
 * 
 */
public class ConfigureManager {

    /**
     * 得到配置管理器的实例，若配置管理器为NULL返回NULL
     * @return 配置管理器实例
     */
    public static ConfigureManager getInstance() {
        return configureManager;
    }

    /**
     * 初始化配置管理器并得到配置管理器的实例
     * @return 配置管理器实例
     */
    public static ConfigureManager newInstance() {
        configureManager = new ConfigureManager();
        return configureManager;
    }

    /**
     * 类的构造函数,对配置文件进行解析,若抛出异常则弹出信息框同时调用errorInit()方法
     */
    private ConfigureManager() {
        try {
            nameMap = NameMap.newInstance();
            language = "english";
            undoStep = 20;
            width = 400;
            height = 300;
            minCost = 3;
            maxCost = 99;
            minkm = 6;
            perkmcost = 0.1;
            virchange = 1;
            init();
        } catch (Exception ex) {
            ex.printStackTrace();
            JOptionPane.showMessageDialog(frame, "WARNING:Configure File Error",
                    "ERROR", JOptionPane.WARNING_MESSAGE);
            errorInit();
        }
    }

    /*
     * 初始化，读取配置文件 
     */
    private void init() throws ParserConfigurationException, SAXException,
            IOException, ConfigureException, NameMapException {
        DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
        doc = db.parse(NAMEMAP_PATH);
        Element root = doc.getDocumentElement();
        parseChild(root);
        doc = db.parse(CONFIGURE_PATH);
        root = doc.getDocumentElement();
        parseChild(root);
    }

    /**
     * 
     * @param root
     * @throws simplemetrosystem.control.Exception.ConfigureException
     * @throws simplemetrosystem.model.Exceptions.NameMapException
     */
    private void parseChild(Element root) throws ConfigureException, NameMapException {
        NodeList children = root.getChildNodes();
        for (int i = 0; i < children.getLength(); i++) {
            Node child = children.item(i);
            if (child instanceof Element) {
                Element childElement = (Element) child;
                if (childElement.getTagName().equals(CON_NAMEMAP)) {
                    parseNameMap(childElement);
                } else if (childElement.getTagName().equals(CON_SYSTEMLANGUAGE)) {
                    parseLanguage(childElement);
                } else if (childElement.getTagName().equals(CON_UNDOSTEP)) {
                    parseUndoStep(childElement);
                } else if (childElement.getTagName().equals(CON_COST)) {
                    parseCost(childElement);
                } else if (childElement.getTagName().equals(CON_CANVAS)) {
                    parseCanvas(childElement);
                } else {
                    throw new ConfigureException();
                }
            }
        }
    }

    /*
     * 解析界面语言标签，
     */
    private void parseNameMap(Element childElement) throws ConfigureException, NameMapException {
        NodeList nameMapNode = childElement.getChildNodes();

        for (int i = 0; i < nameMapNode.getLength(); i++) {
            Node child = nameMapNode.item(i);
            if (child instanceof Element) {
                Element nameElement = (Element) child;
                if (nameElement.getTagName().equals(CON_LANGUAGE)) {
                    Text textNode = (Text) nameElement.getFirstChild();
                    nameMap.addLanguage(textNode.getData().trim());
                } else if (nameElement.getTagName().equals(CON_INDEX)) {
                    parseIndex(nameElement);
                } else {
                    throw new ConfigureException();
                }
            }
        }
    }

    /*
     * 解析索引标签
     */
    private void parseIndex(Element nameElement) throws NameMapException {
        NodeList indexNode = nameElement.getChildNodes();

        String index = null;

        for (int i = 0; i < indexNode.getLength(); i++) {
            Node child = indexNode.item(i);
            if (child instanceof Element) {
                Element indexElement = (Element) child;
                if (indexElement.getTagName().equals(NameMap.ENGLISH)) {
                    Text textNode = (Text) indexElement.getFirstChild();
                    index = textNode.getData().trim();
                } else {
                    Text textNode = (Text) indexElement.getFirstChild();
                    nameMap.addName(index, textNode.getData().trim(), indexElement.getTagName());
                }
            }
        }
    }

    /**
     * 解析语音标签
     * @param childElement
     * @throws simplemetrosystem.control.Exception.ConfigureException
     * @throws simplemetrosystem.model.Exceptions.NameMapException
     */
    private void parseLanguage(Element childElement) throws ConfigureException, NameMapException {
        Text textNode = (Text) childElement.getFirstChild();
        String languageText = textNode.getData().trim();
        nameMap.setLanguage(languageText);
        this.language = languageText;
    }

    /**
     * 
     * @param childElement
     * @throws simplemetrosystem.control.Exception.ConfigureException
     */
    private void parseUndoStep(Element childElement) throws ConfigureException {
        Text textNode = (Text) childElement.getFirstChild();
        undoStep = Integer.parseInt(textNode.getData().trim());
    }

    private void parseCanvas(Element childElement) throws ConfigureException {
        NodeList canvasNode = childElement.getChildNodes();

        for (int i = 0; i < canvasNode.getLength(); i++) {
            Node child = canvasNode.item(i);
            if (child instanceof Element) {
                Element canvasElement = (Element) child;
                if (canvasElement.getTagName().equals(CAN_WIDTH)) {
                    Text textNode = (Text) canvasElement.getFirstChild();
                    width = Integer.parseInt(textNode.getData().trim());
                } else if (canvasElement.getTagName().equals(CAN_HEIGHT)) {
                    Text textNode = (Text) canvasElement.getFirstChild();
                    height = Integer.parseInt(textNode.getData().trim());
                } else {
                    throw new ConfigureException();
                }
            }
        }
    }

    private void parseCost(Element childElement) throws ConfigureException {
        NodeList costNode = childElement.getChildNodes();

        for (int i = 0; i < costNode.getLength(); i++) {
            Node child = costNode.item(i);
            if (child instanceof Element) {
                Element costElement = (Element) child;
                if (costElement.getTagName().equals(COS_MINCOST)) {
                    Text textNode = (Text) costElement.getFirstChild();
                    minCost = Integer.parseInt(textNode.getData().trim());
                } else if (costElement.getTagName().equals(COS_MAXCOST)) {
                    Text textNode = (Text) costElement.getFirstChild();
                    maxCost = Integer.parseInt(textNode.getData().trim());
                } else if (costElement.getTagName().equals(COS_MINKM)) {
                    Text textNode = (Text) costElement.getFirstChild();
                    minkm = Double.parseDouble(textNode.getData().trim());
                } else if (costElement.getTagName().equals(COS_PERKMCOST)) {
                    Text textNode = (Text) costElement.getFirstChild();
                    perkmcost = Double.parseDouble(textNode.getData().trim());
                } else if (costElement.getTagName().equals(COS_VIRCHANGE)) {
                    Text textNode = (Text) costElement.getFirstChild();
                    virchange = Integer.parseInt(textNode.getData().trim());
                } else {
                    throw new ConfigureException();
                }
            }
        }
    }

    /**
     * 
     * @return
     */
    public int getUndoStep() {
        return undoStep;
    }

    /**
     * 
     * @param undoStep 
     */
    public void setUndoStep(int undoStep) {
        this.undoStep = undoStep;
    }

    public int getHeight() {
        return height;
    }

    public void setHeight(int height) {
        this.height = height;
    }

    public int getMaxCost() {
        return maxCost;
    }

    public void setMaxCost(int maxCost) {
        this.maxCost = maxCost;
    }

    public int getMinCost() {
        return minCost;
    }

    public void setMinCost(int minCost) {
        this.minCost = minCost;
    }

    public double getMinkm() {
        return minkm;
    }

    public void setMinkm(double minkm) {
        this.minkm = minkm;
    }

    public double getPerkmcost() {
        return perkmcost;
    }

    public void setPerkmcost(double perkmcost) {
        this.perkmcost = perkmcost;
    }

    public int getWidth() {
        return width;
    }

    public void setWidth(int width) {
        this.width = width;
    }

    public String getLanguage() {
        return language;
    }

    public void setLanguage(String language) {
        this.language = language;
    }

    public int getVirchange() {
        return virchange;
    }

    public void setVirchange(int virchange) {
        this.virchange = virchange;
    }

    public void saveConfigure() {
        try {
            Document newDoc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
            Element root = newDoc.createElement("root");

            Element languageElement = newDoc.createElement(CON_SYSTEMLANGUAGE);
            root.appendChild(languageElement);
            Text languageText = newDoc.createTextNode(language);
            languageElement.appendChild(languageText);

            Element undoElement = newDoc.createElement(CON_UNDOSTEP);
            root.appendChild(undoElement);
            Text undoText = newDoc.createTextNode("" + undoStep);
            undoElement.appendChild(undoText);

            Element costElement = newDoc.createElement(CON_COST);
            root.appendChild(costElement);

            Element minCostElement = newDoc.createElement(COS_MINCOST);
            costElement.appendChild(minCostElement);
            Text minCostText = newDoc.createTextNode("" + minCost);
            minCostElement.appendChild(minCostText);

            Element maxCostElement = newDoc.createElement(COS_MAXCOST);
            costElement.appendChild(maxCostElement);
            Text maxCostText = newDoc.createTextNode("" + maxCost);
            maxCostElement.appendChild(maxCostText);

            Element minKMElement = newDoc.createElement(COS_MINKM);
            costElement.appendChild(minKMElement);
            Text minKMText = newDoc.createTextNode("" + minkm);
            minKMElement.appendChild(minKMText);

            Element perCostElement = newDoc.createElement(COS_PERKMCOST);
            costElement.appendChild(perCostElement);
            Text perCostText = newDoc.createTextNode("" + perkmcost);
            perCostElement.appendChild(perCostText);

            Element virchangeElement = newDoc.createElement(COS_VIRCHANGE);
            costElement.appendChild(virchangeElement);
            Text virchangeText = newDoc.createTextNode("" + virchange);
            virchangeElement.appendChild(virchangeText);

            Element canvasElement = newDoc.createElement(CON_CANVAS);
            root.appendChild(canvasElement);

            Element widElement = newDoc.createElement(CAN_WIDTH);
            canvasElement.appendChild(widElement);
            Text widText = newDoc.createTextNode("" + width);
            widElement.appendChild(widText);

            Element heiElement = newDoc.createElement(CAN_HEIGHT);
            canvasElement.appendChild(heiElement);
            Text heiText = newDoc.createTextNode("" + height);
            heiElement.appendChild(heiText);

            newDoc.appendChild(root);

            File comfXML = new File(CONFIGURE_PATH);
            comfXML.createNewFile();

            Transformer tran = TransformerFactory.newInstance().newTransformer();
            tran.transform(new DOMSource(newDoc), new StreamResult(new FileOutputStream(comfXML)));


        } catch (IOException ex) {
            Logger.getLogger(ConfigureManager.class.getName()).log(Level.SEVERE, null, ex);
        } catch (TransformerException ex) {
            ex.printStackTrace();
        } catch (ParserConfigurationException ex) {
            ex.printStackTrace();
        }
    }

    /*
     * 当解析默认配置文件产生错误时，执行该方法，进行默认配置，仅支持英文界面
     */
    private void errorInit() {
        nameMap = NameMap.newInstance();
        language = "english";
        undoStep = 20;
        width = 400;
        height = 300;
        minCost = 3;
        maxCost = 99;
        minkm = 6;
        perkmcost = 0.1;
        virchange = 1;
    }
    private static ConfigureManager configureManager;
    private NameMap nameMap;
    private Document doc;
    private MetroFrame frame;
    private String language;
    private int undoStep;
    private int width;
    private int height;
    private int minCost;
    private int maxCost;
    private double minkm;
    private double perkmcost;
    private int virchange;
    /**
     * 配置文件默认所在地址
     */
    public static final String CONFIGURE_PATH = "configure/configure.xml";
    /**
     * 
     */
    public static final String NAMEMAP_PATH = "configure/NameMap.xml";
    /**
     * 语言信息标签名
     */
    public static final String CON_NAMEMAP = "NameMap";
    /**
     * 系统语言标签名
     */
    public static final String CON_SYSTEMLANGUAGE = "SystemLanguage";
    /**
     * 撤销次数标签名
     */
    public static final String CON_UNDOSTEP = "UndoStep";
    /**
     * 语言种类标签
     */
    public static final String CON_LANGUAGE = "language";
    /**
     * 索引标签
     */
    public static final String CON_INDEX = "index";
    /**
     * 画板属性标签
     */
    public static final String CON_CANVAS = "Canvas";
    /**
     * 画板宽度标签
     */
    public static final String CAN_WIDTH = "width";
    /**
     * 画板高度标签
     */
    public static final String CAN_HEIGHT = "height";
    /**
     * 计费标签
     */
    public static final String CON_COST = "Cost";
    /**
     * 最小计费额标签
     */
    public static final String COS_MINCOST = "mincost";
    /**
     * 最小计费里程表标签
     */
    public static final String COS_MINKM = "minkm";
    /**
     * 最大计费额标签
     */
    public static final String COS_MAXCOST = "maxcost";
    /**
     * 每公里计费标签
     */
    public static final String COS_PERKMCOST = "perkmcost";
    /**
     * 虚拟换乘标签
     */
    public static final String COS_VIRCHANGE = "virchange";
}
